#ifndef MATRIX_H
#define MATRIX_H

#include <stdint.h>
#include "QuantizationMatrix.h"

using Eigen::MatrixXd;

class Matrix
{
public:
    Matrix() {}
    Matrix(int columnLength);
	Matrix(int columnLength, const MatrixXd& m);
    Matrix(const Matrix &m);
    ~Matrix() {}

    /*
    ** When using operator[] mind the way the matrix is defined.
    ** The matrix is column major.
    */
    const double &operator[]( const int i ) const { return _matrix(i); }
    const double &getRowMajorElement(const int i) const;


    Matrix& operator=(Matrix &matrix);
    Matrix operator+(Matrix &matrix) const;
    Matrix operator-(Matrix &matrix) const;
    Matrix operator*(Matrix &matrix) const;

    // Dequantizate matrix
    Matrix operator*( QuantizationMatrix &matrix ) const;

    // Quantizate matrix
    Matrix operator/( QuantizationMatrix &matrix ) const;

    void loadIdentity();
    /*
    ** When using setMatrix mind the way the matrix is defined.
    ** The matrix is column major.
    */
    void setMatrix(MatrixXd& matrix);
    void addColumn(uint8_t *column, int columnNumber);
    void round();
    void normalize();
    void deNormalize();
    void flattenHighValues();

    /*
    ** When using setMatrixRowMajor note that the row major array is inputted in the column major array of this class.
    ** The class matrix is column major.
    */
    void setMatrixRowMajor(double *matrix);
    void setMatrixRowMajor(uint8_t *matrix);

    uint8_t* getMatrixData();
    const MatrixXd& getMatrix()const{return _matrix;}
    const int size() const { return _size; }
    const int columnLength() const { return _columnLength; }

    /*
    ** Returns a list of the matrix elements in a zigzag order.
    */
    int8_t *zigzag();

    void setSize(int size);

    void setElement(int i, double e);

private:
    MatrixXd _matrix;
    int _size;
    int _columnLength;

    void newMove1(int &c, int &r, int &cMoveMem, int &rMoveMem, int &move);
    void newMove2(int &c, int &r, int &cMoveMem, int &rMoveMem, int &move);
    double calculateMatrixMultElement (int pos, const double* first, const double* second) const;
};

#endif // MATRIX_H
